{
  lib,
  pkgs,
  config,
  ...
}:
with lib;
let
  cfg = config.services.hledger-web;
in
{
  options.services.hledger-web = {

    enable = mkEnableOption "hledger-web service";

    serveApi = mkEnableOption "serving only the JSON web API, without the web UI";

    host = mkOption {
      type = types.str;
      default = "127.0.0.1";
      description = ''
        Address to listen on.
      '';
    };

    port = mkOption {
      type = types.port;
      default = 5000;
      example = 80;
      description = ''
        Port to listen on.
      '';
    };

    allow = mkOption {
      type = types.enum [
        "view"
        "add"
        "edit"
        "sandstorm"
      ];
      default = "view";
      description = ''
        User's access level for changing data.

        * view: view only permission.
        * add: view and add permissions.
        * edit: view, add, and edit permissions.
        * sandstorm: permissions from the `X-Sandstorm-Permissions` request header.
      '';
    };

    stateDir = mkOption {
      type = types.path;
      default = "/var/lib/hledger-web";
      description = ''
        Path the service has access to. If left as the default value this
        directory will automatically be created before the hledger-web server
        starts, otherwise the sysadmin is responsible for ensuring the
        directory exists with appropriate ownership and permissions.
      '';
    };

    journalFiles = mkOption {
      type = types.listOf types.str;
      default = [ ".hledger.journal" ];
      description = ''
        Paths to journal files relative to {option}`services.hledger-web.stateDir`.
      '';
    };

    baseUrl = mkOption {
      type = with types; nullOr str;
      default = null;
      example = "https://example.org";
      description = ''
        Base URL, when sharing over a network.
      '';
    };

    extraOptions = mkOption {
      type = types.listOf types.str;
      default = [ ];
      example = [ "--forecast" ];
      description = ''
        Extra command line arguments to pass to hledger-web.
      '';
    };

  };

  imports = [
    (mkRemovedOptionModule [
      "services"
      "hledger-web"
      "capabilities"
    ] "This option has been replaced by new option `services.hledger-web.allow`.")
  ];

  config = mkIf cfg.enable {

    users.users.hledger = {
      name = "hledger";
      group = "hledger";
      isSystemUser = true;
      home = cfg.stateDir;
      useDefaultShell = true;
    };

    users.groups.hledger = { };

    systemd.services.hledger-web =
      let
        serverArgs =
          with cfg;
          escapeShellArgs (
            [
              "--serve"
              "--host=${host}"
              "--port=${toString port}"
              "--allow=${allow}"
              (optionalString (cfg.baseUrl != null) "--base-url=${cfg.baseUrl}")
              (optionalString (cfg.serveApi) "--serve-api")
            ]
            ++ (map (f: "--file=${stateDir}/${f}") cfg.journalFiles)
            ++ extraOptions
          );
      in
      {
        description = "hledger-web - web-app for the hledger accounting tool.";
        documentation = [
          "info:hledger-web"
          "man:hledger-web(1)"
          "https://hledger.org/hledger-web.html"
        ];
        wantedBy = [ "multi-user.target" ];
        after = [ "network.target" ];
        serviceConfig = mkMerge [
          {
            ExecStart = "${pkgs.hledger-web}/bin/hledger-web ${serverArgs}";
            Restart = "always";
            WorkingDirectory = cfg.stateDir;
            User = "hledger";
            Group = "hledger";
            PrivateTmp = true;
          }
          (mkIf (cfg.stateDir == "/var/lib/hledger-web") {
            StateDirectory = "hledger-web";
          })
        ];
      };

  };

  meta.maintainers = with lib.maintainers; [
    marijanp
    erictapen
  ];
}
